package com.example.oauth2.config.oauth2;

import com.example.oauth2.common.constant.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @description： 权限认证服务配置
 * @author: xuwenqian
 * @date: 2020/4/15 14:26
 */
@Configuration
@EnableAuthorizationServer //  注解开启验证服务器 提供/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
     * 该对象将为刷新token提供支持
     */
    @Autowired(required = false)
    private UserDetailsService userDetailsService;
    /**
     * 该对象用来支持 password 模式
     */
    @Autowired
    private AuthenticationManager authenticationManager;
    /**
     * 过期时间
     */
    @Value("${oauth2.tokenTimeout:3600}")
    private int expirationTime;
    @Value("${oauth2.client.client-id}")
    private String clientId;
    @Value("${oauth2.client.client-secret}")
    private String clientSecret;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置 password 授权模式
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient(clientId)
                .secret(passwordEncoder()
                        .encode(clientSecret))
                .accessTokenValiditySeconds(expirationTime)
                .refreshTokenValiditySeconds(expirationTime)
                .scopes("all")
                //授权模式为授权码模式和password和refresh_token两种
                .authorizedGrantTypes("authorization_code","password", "refresh_token")
                .authorities("USER")
                .resourceIds(Constants.RESOURCE_ID);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
        configurer.tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter())
                //支持GET  POST  请求获取token
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.OPTIONS)
                //开启刷新token
                .reuseRefreshTokens(true);
        configurer.authenticationManager(authenticationManager);
        configurer.userDetailsService(userDetailsService);

    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        // 表示支持 client_id 和 client_secret 做登录认证
        security.allowFormAuthenticationForClients();
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(Constants.signingKey);
        return converter;
    }
}
